// Copyright (c) ZeroC, Inc.

using System.Diagnostics;
using System.Text;

namespace Test;

public abstract class TestHelper
{
    // A custom trace listener that always aborts the application upon failure.
    internal class TestTraceListener : DefaultTraceListener
    {
        public override void Fail(string message) => TestHelper.fail(message, null);

        public override void Fail(string message, string detailMessage) => TestHelper.fail(message, detailMessage);
    }

    static TestHelper()
    {
        // Replace the default trace listener that is responsible of displaying the retry/abort dialog
        // with our custom trace listener that always aborts upon failure.
        // see: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.defaulttracelistener?view=net-5.0#remarks
        Trace.Listeners.Clear();
        Trace.Listeners.Add(new TestTraceListener());
    }

    public virtual void run(string[] args) => throw new NotImplementedException();

    public virtual Task runAsync(string[] args)
    {
        run(args);
        return Task.CompletedTask;
    }

    public string getTestEndpoint(int num = 0, string protocol = "") =>
        getTestEndpoint(_communicator.getProperties(), num, protocol);

    public static string getTestEndpoint(Ice.Properties properties, int num = 0, string protocol = "")
    {
        var sb = new StringBuilder();
        sb.Append(protocol.Length == 0 ? properties.getIceProperty("Ice.Default.Protocol") :
                                   protocol);
        sb.Append(" -p ");
        sb.Append(properties.getPropertyAsIntWithDefault("Test.BasePort", 12010) + num);
        return sb.ToString();
    }

    public string getTestHost() => getTestHost(_communicator.getProperties());

    public static string getTestHost(Ice.Properties properties) =>
        properties.getPropertyWithDefault("Ice.Default.Host", "127.0.0.1");

    public string getTestProtocol() => getTestProtocol(_communicator.getProperties());

    public static string getTestProtocol(Ice.Properties properties) =>
        properties.getIceProperty("Ice.Default.Protocol");

    public int getTestPort(int num) => getTestPort(_communicator.getProperties(), num);

    public static int getTestPort(Ice.Properties properties, int num) =>
        properties.getPropertyAsIntWithDefault("Test.BasePort", 12010) + num;

    public static void updateLogFileProperty(Ice.Properties properties, string suffix)
    {
        string logFile = properties.getIceProperty("Ice.LogFile");
        if (logFile.Length > 0)
        {
            int dotIndex = logFile.LastIndexOf('.');
            string newLogFile = dotIndex == -1
                ? $"{logFile}{suffix}"
                : logFile.Insert(dotIndex, suffix);
            properties.setProperty("Ice.LogFile", newLogFile);
        }
    }

    public TextWriter getWriter() => Console.Out;

    public Ice.Properties createTestProperties(ref string[] args)
    {
        var properties = new Ice.Properties(ref args);
        args = properties.parseCommandLineOptions("Test", args);
        return properties;
    }

    public Ice.Communicator initialize(ref string[] args)
    {
        var initData = new Ice.InitializationData
        {
            properties = createTestProperties(ref args)
        };
        return initialize(initData);
    }

    public Ice.Communicator initialize(Ice.Properties properties)
    {
        var initData = new Ice.InitializationData
        {
            properties = properties
        };
        return initialize(initData);
    }

    public Ice.Communicator initialize(Ice.InitializationData initData)
    {
        var communicator = new Ice.Communicator(initData);
        _communicator ??= communicator;
        return communicator;
    }

    public Ice.Communicator communicator() => _communicator;

    public static void test(bool b)
    {
        if (!b)
        {
            fail(null, null);
        }
    }

    internal static void fail(string message, string detailMessage)
    {
        var sb = new StringBuilder();
        sb.Append("failed:\n");
        if (message != null && message.Length > 0)
        {
            sb.Append("message: ").Append(message).Append('\n');
        }
        if (detailMessage != null && detailMessage.Length > 0)
        {
            sb.Append("details: ").Append(detailMessage).Append('\n');
        }
        try
        {
            sb.Append(new StackTrace(fNeedFileInfo: true).ToString()).Append('\n');
        }
        catch
        {
        }

        Console.WriteLine(sb.ToString());
        Environment.Exit(1);
    }

    public void serverReady()
    {
    }

    private Ice.Communicator _communicator;
}

public abstract class AllTests
{
    protected static void test(bool b)
    {
        if (!b)
        {
            Debug.Assert(false);
            throw new Exception();
        }
    }
}

public static class TestDriver
{
    public static async Task<int> runTestAsync<T>(string[] args)
        where T : TestHelper, new()
    {
        int status = 0;
        try
        {
            var testHelper = new T();
            await testHelper.runAsync(args);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            status = 1;
        }
        return status;
    }
}
